package copyfile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.FileLock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MappedByteBufferDemo {
	public static void main(String[] args) throws FileNotFoundException,
			IOException, InterruptedException {

		// String sourcefile = "C:\\a.rmvb";
		// String targetfile = "C:\\b.rmvb";
		String sourcefile = "C:\\code\\studio2010\\shutdownerrorwindows\\Debug\\shutdownerrorwindows.dll";
		String targetfile = "C:\\shutdownerrorwindows.dll";
		File f = new File(sourcefile);
		RandomAccessFile raf = new RandomAccessFile(targetfile, "rw");
		raf.setLength(f.length());// 1K
		System.out.println("大小：" + f.length());
		// raf.close();
		//MutilThreadRW(sourcefile, targetfile, raf);// 14691.0毫秒 7738.0毫秒
		NioCopyFile(sourcefile,targetfile);//12403.0毫秒 10156.0毫秒
	}

	public static void targetPath(String source,String target) throws Exception{
        int length=2097152;
        FileInputStream in=new FileInputStream(new File(source) );
        RandomAccessFile out=new RandomAccessFile(new File(target),"rw");
        FileChannel inC=in.getChannel();
        MappedByteBuffer outC=null;
        MappedByteBuffer inbuffer=null;
        byte[] b=new byte[length];
        while(true){
            if(inC.position()==inC.size()){
                inC.close();
                outC.force();
                out.close();
                return;
            }
            if((inC.size()-inC.position())<length){
                length=(int)(inC.size()-inC.position());
            }else{
                length=20971520;
            }
            b=new byte[length];
            inbuffer=inC.map(MapMode.READ_ONLY,inC.position(),length);
            inbuffer.load();
            inbuffer.get(b);
            outC=out.getChannel().map(MapMode.READ_WRITE,inC.position(),length);
            inC.position(b.length+inC.position());
            outC.put(b);
            outC.force();
        }
    }
	
	
	// ***********************************************************************************
	public static void NioCopyFile(String sourcePath, String targetPath)
			throws FileNotFoundException, IOException {
		final long ModuleSize = 1024 * 1024 * 4;// 额定读写大小4M
		long before = System.currentTimeMillis();

		File files = new File(sourcePath); // 源文件
		File filet = new File(targetPath); // 目标文件

		long size = files.length(); // 文件总大小
		int copycount = (int) (size / ModuleSize); // 获取读、写
		if (size % ModuleSize != 0) {
			copycount = copycount + 1;
		}

		int copynum = copycount;

		long countSize = ModuleSize; // 每块分割大小<每次读写的大小>
		long lontemp = countSize; // 初始读、写大小
		FileChannel channels = new RandomAccessFile(files, "r").getChannel(); // 得到映射读文件的通道
		FileChannel channelt = new RandomAccessFile(filet, "rw").getChannel(); // 得到映射写文件的通道

		long j = 0; // 每次循环累加字节的起始点
		MappedByteBuffer mbbs = null; // 声明读源文件对象
		MappedByteBuffer mbbt = null; // 声明写目标文件对象

		while (j < size) {
			mbbs = channels.map(FileChannel.MapMode.READ_ONLY, j, lontemp); // 每次读源文件都重新构造对象
			mbbt = channelt.map(FileChannel.MapMode.READ_WRITE, j, lontemp); // 每次写目标文件都重新构造对象
			for (int i = 0; i < lontemp; i++) {
				byte b = mbbs.get(i); // 从源文件读取字节
				mbbt.put(i, b); // 把字节写到目标文件中
			}
			System.gc(); // 手动调用 GC <必须的，否则出现异常>
			System.runFinalization(); // 运行处于挂起终止状态的所有对象的终止方法。<必须的，否则出现异常>
			j += lontemp; // 累加每次读写的字节
			lontemp = size - j; // 获取剩余字节
			lontemp = lontemp > countSize ? countSize : lontemp; // 如果剩余字节 大于
			// 每次分割字节 则
			// 读取 每次分割字节
			// ，否则
			// 读取剩余字节
		}

		System.out.println("MillTime : "
				+ (double) (System.currentTimeMillis() - before) + "毫秒");
	}

	// ***********************************************************************************
	public static void MutilThreadRW(String sourcePath, String targetPath,
			RandomAccessFile raf) throws FileNotFoundException, IOException,
			InterruptedException {
		final long ModuleSize = 1024 * 1024 * 4;// 额定读写大小1M
		long before = System.currentTimeMillis();
		File files = new File(sourcePath); // 源文件
		File filet = new File(targetPath); // 目标文件
		long size = files.length(); // 文件总大小
		int copycount = (int) (size / ModuleSize); // 获取读、写
		long lastModuleSize = size % ModuleSize;// 最后一个线程负责的大小
		if (lastModuleSize != 0) {
			copycount = copycount + 1;
		}
		int copynum = copycount;
		long countSize = ModuleSize; // 每块分割大小<每次读写的大小>
		long lontemp = countSize; // 初始读、写大小
		FileChannel readchannel = new RandomAccessFile(files, "r").getChannel(); // 得到映射读文件的通道
		FileChannel writechannel = new RandomAccessFile(filet, "rw")
				.getChannel(); // 得到映射写文件的通道
		ExecutorService executorService = Executors.newFixedThreadPool(10);
		for (int i = 0; i < copycount; i++) {
			if (i < (copycount - 1)) {
				executorService.execute(new RWUtil(readchannel, writechannel,
						ModuleSize * i, ModuleSize));
			} else {
				executorService.execute(new RWUtil(readchannel, writechannel,
						ModuleSize * i, lastModuleSize));
			}
		}
		executorService.shutdown();
		executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);// 所有线程执行完毕才进入下一步
		System.out.println("MillTime : "
				+ (double) (System.currentTimeMillis() - before) + "毫秒");
	}

}

class RWUtil implements Runnable {
	long begin;
	long end;
	FileChannel readchannel;
	FileChannel writechannel;

	RWUtil(FileChannel readchannel, FileChannel writechannel, long begin,
			long end) {
		this.readchannel = readchannel;
		this.writechannel = writechannel;
		this.begin = begin;
	}

	public void run() {
		FileLock lock = null;
		try {
			MappedByteBuffer readFile = readchannel.map(
					FileChannel.MapMode.READ_ONLY, begin, end); // 每次读源文件都重新构造对象
			MappedByteBuffer writeFile = writechannel.map(
					FileChannel.MapMode.READ_WRITE, begin, end); // 每次写目标文件都重新构造对象
//			for (int i = 0; i < end; i++) {
//				byte b = readFile.get(i); // 从源文件读取字节
//				writeFile.put(i, b); // 把字节写到目标文件中
//			}
			 writeFile.put(readFile);
			System.gc(); // 手动调用 GC <必须的，否则出现异常>
			System.runFinalization(); // 运行处于挂起终止状态的所有对象的终止方法。<必须的，否则出现异常>
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
// /*
// 1.文件锁的分类
// 文件锁分为阻塞式文件锁和非阻塞式的文件锁，可以通过new FileChannel().tryLock
// /lock,后者是阻塞式，阻塞式意思是指当前进程没有获得文件锁即刻等待直到有进程将对应文件的锁释放。
//
// 2.文件锁针对进程
// 这里指进程的原因是因为对文件锁而言一个线程同一时间段对同一个文件只能加上一把锁，只有等待当前线程释放掉后，才能继续对文件加锁，不然会报OverlappingFileLockException的错误，所以文件锁是进程间的锁。
//
// 3.线程间预防重复加锁，减少读写文件等待时间
// //给该文件加锁
// RandomAccessFile fis = new RandomAccessFile(file, "rws"); //单一线程的读写同步
// FileChannel fcin=fis.getChannel(); // 获得文件通道
// FileLock flin=null; //声明文件锁对象
// int operateNum=10; //若文件锁一直别占用，设置最大读取次数为10次，超出次数表示文 //件不可读,
// For(int i=0;i<operateNum,i++){
// try {
// flin = fcin.tryLock(); // 获取文件非阻塞锁，如果不能获取，继续等待0.5s.
// break;
// } catch (Exception e) {
// System.out.println("有其他线程正在操作该文件，当前线程休眠500毫秒");
// sleep(500);
// If((i+1)>=operateNum){
// Throw e //文件被强制占用 ，处于不可读的状态
// }
// }
// }
// //获取成功
// //进行文件的读或写的操作
// RandomAccessFile 流中读出文件的数据；
// RandomAccessFile 流向该文件写入新数据；
// //该文件的操作完毕，释放该文件锁和相关资源
// flin.release();
// fcout.close();
// out.close();
// 4.线程写文件加锁后，读文件线程不需加锁的方法（保证读写同步）
// 写线程：
// RandomAccessFile fos=new RandomAccessFile(file,"rws");
// FileChannel fileC=fos.getChannel();
// FileLock fileL=null;
// while(true){
// try{
// fileL=fileC.lock();
// break;
// }catch(Exception e){
// try {
// System.out.println("**********************************文件被操作，写文件线程休眠0.2m");
// Thread.sleep(200);
// } catch (InterruptedException e1) {
// e1.printStackTrace();
// }
// }
// }
// /*注意此种方法写线程必须使用RandomAccessFile，FileOutputStream 会报错，可能是读文件时使用文件映射，为保证读写*/
// 读线程：
// RandomAccessFile fis=new RandomAccessFile(file,"rws");
// MappedByteBuffer mbb=fis.getChannel().map(FileChannel.MapMode.READ_WRITE, 0,
// (int)fis.length())；
// byte[] buf = new byte[(int) fis.length()];
// for(int i=0;i<fis.length();i++){
// buf[i]= mbb.get(i);
// }
// 注意此方法读线程使用FileInputStream 好像有文件不能同步的问题
// */